home *** CD-ROM | disk | FTP | other *** search
- /*
- * from_sock() determines the type of socket (datagram, stream), the name
- * and address of the host at the other end of standard input, and the
- * remote user name (if RFC 931 lookups are enabled). A host name of "stdin"
- * is returned if the program is run from a tty. All results are in static
- * or allocated memory. from_sock() assumes that its result argument is
- * already initialized with default values.
- *
- * The return status is (-1) if the remote host pretends to have someone elses
- * host name, otherwise a zero status is returned.
- *
- * Diagnostics are reported through syslog(3).
- *
- * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
- */
-
- #ifndef lint
- static char sccsid[] = "@(#) from_sock.c 1.1 93/07/02 16:33:48";
- #endif
-
- /* System libraries. */
-
- #include <sys/types.h>
- #include <sys/param.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include <stdio.h>
- #include <syslog.h>
- #include <errno.h>
-
- extern char *inet_ntoa();
- extern char *strncpy();
- extern char *strcpy();
-
- /* In case not defined in <sys/param.h>. */
-
- #ifndef MAXHOSTNAMELEN
- #define MAXHOSTNAMELEN 1024 /* string with host name */
- #endif
-
- /* Local stuff. */
-
- #include "log_tcp.h"
-
- /* Forward declarations. */
-
- static int ip_match();
- static void udp_sink();
-
- /* The following are to be used in assignment context, not in comparisons. */
-
- #define GOOD 1
- #define BAD 0
-
- /*
- * With early SunOS 5 versions, recvfrom() does not completely fill in the
- * source address structure when doing a non-destructive read. The following
- * code works around the problem. It does no harm on "normal" systems.
- */
-
- #ifdef RECVFROM_BUG
-
- static int fix_recvfrom(sock, buf, buflen, flags, from, fromlen)
- int sock;
- char *buf;
- int buflen;
- int flags;
- struct sockaddr *from;
- int *fromlen;
- {
- int ret;
-
- /* Assume that both ends of a socket belong to the same address family. */
-
- if ((ret = recvfrom(sock, buf, buflen, flags, from, fromlen)) >= 0) {
- if (from->sa_family == 0) {
- struct sockaddr my_addr;
- int my_addr_len = sizeof(my_addr);
-
- if (getsockname(0, &my_addr, &my_addr_len)) {
- syslog(LOG_ERR, "getsockname: %m");
- } else {
- from->sa_family = my_addr.sa_family;
- }
- }
- }
- return (ret);
- }
-
- #define recvfrom fix_recvfrom
- #endif
-
- /*
- * The Apollo SR10.3 and some SYSV4 getpeername(2) versions do not return an
- * error in case of a datagram-oriented socket. Instead, they claim that all
- * UDP requests come from address 0.0.0.0. The following code works around
- * the problem. It does no harm on "normal" systems.
- */
-
- #ifdef GETPEERNAME_BUG
-
- static int fix_getpeername(sock, sa, len)
- int sock;
- struct sockaddr *sa;
- int *len;
- {
- int ret;
- struct sockaddr_in *sin = (struct sockaddr_in *) sa;
-
- if ((ret = getpeername(sock, sa, len)) >= 0
- && sa->sa_family == AF_INET
- && sin->sin_addr.s_addr == 0) {
- errno = ENOTCONN;
- return (-1);
- } else {
- return (ret);
- }
- }
-
- #define getpeername fix_getpeername
- #endif
-
- /* from_sock - find out what is at the other end of standard input */
-
- int from_sock(f)
- struct from_host *f;
- {
- static struct sockaddr sa;
- struct sockaddr_in *sin = (struct sockaddr_in *) (&sa);
- int length = sizeof(sa);
- char buf[BUFSIZ];
-
- /*
- * Look up the remote host address. Hal R. Brand <BRAND@addvax.llnl.gov>
- * suggested how to get the remote host info in case of UDP connections:
- * peek at the first message without actually looking at its contents.
- */
-
- if (getpeername(0, &sa, &length) >= 0) { /* assume TCP request */
- f->sock_type = FROM_CONNECTED;
- } else {
- switch (errno) {
- default:
- if (isatty(0)) /* stdin is not a socket */
- f->name = "stdin";
- else
- syslog(LOG_ERR, "getpeername: %m"); /* other, punt */
- return (0);
- case ENOTCONN: /* assume UDP request */
- length = sizeof(sa);
- if (recvfrom(0, buf, sizeof(buf), MSG_PEEK, &sa, &length) < 0) {
- syslog(LOG_ERR, "recvfrom: %m");
- return (0);
- }
- #ifdef really_paranoid
- memset(buf, 0 sizeof(buf));
- #endif
- f->sock_type = FROM_UNCONNECTED;
- f->sink = udp_sink;
- break;
- }
- }
-
- /*
- * At present, we can only deal with the AF_INET address family. Some
- * implementations of System V religion never fill in the address family
- * field in case of UDP connections. If that happens, you may want to
- * take the chance and assume that we're dealing with TCP/IP anyway.
- */
-
- #ifdef ADDRESS_FAMILY_BUG
- if (sa.sa_family == 0)
- sa.sa_family = AF_INET;
- #endif
- if (sa.sa_family != AF_INET) {
- syslog(LOG_ERR, "unexpected address family %ld", (long) sa.sa_family);
- return (0);
- }
- return (ip_host(f, sin));
- }
-
- /* ip_host - map an IP address structure to human-readable address and name */
-
- int ip_host(f, sin)
- struct from_host *f;
- struct sockaddr_in *sin;
- {
- static char addr_buf[FROM_ADDRLEN];
- static char name_buf[MAXHOSTNAMELEN];
- struct hostent *hp;
-
- /*
- * Keep the client sockaddr info in case we want to look up the remote
- * user. Save the host address. A later inet_ntoa() call may clobber it.
- */
-
- f->sin = sin;
- f->addr = strcpy(addr_buf, inet_ntoa(sin->sin_addr));
-
- /*
- * Look up the remote user name. Does not work for UDP services, and may
- * cause noticeable delays with non-UNIX clients.
- */
-
- #ifdef RFC931
- if (f->sock_type == FROM_CONNECTED)
- f->user = rfc931_name(sin);
- #endif
-
- /*
- * Look up the remote host name and make a copy, or a later
- * gethostbyxxx() call may clobber it.
- */
-
- if ((hp = gethostbyaddr((char *) &sin->sin_addr,
- sizeof(sin->sin_addr),
- AF_INET)) == 0) {
- return (0);
- }
- f->name = strncpy(name_buf, hp->h_name, sizeof(name_buf) - 1);
- name_buf[sizeof(name_buf) - 1] = 0;
-
- /*
- * Verify that the host name does not belong to someone else. If host
- * name verification fails, pretend that the host name lookup failed.
- */
-
- if (ip_match(f->name, sin->sin_addr)) {
- return (0);
- } else {
- f->name = FROM_UNKNOWN;
- return (-1); /* verification failed */
- }
- }
-
- /* ip_match - determine if host name matches IP address */
-
- static int ip_match(remotehost, addr)
- char *remotehost;
- struct in_addr addr;
- {
- struct hostent *hp;
- int i;
-
- if ((hp = gethostbyname(remotehost)) == 0) {
-
- /*
- * Unable to verify that the host name matches the address. This may
- * be a transient problem or a botched name server setup. We decide
- * to play safe.
- */
-
- syslog(LOG_ERR, "gethostbyname(%s): host address lookup failed",
- remotehost);
- return (BAD);
-
- } else {
-
- /*
- * Make sure that gethostbyname() returns the "correct" host name.
- * Unfortunately, gethostbyname("localhost") sometimes yields
- * "localhost.domain". Since the latter host name comes from the
- * local DNS, we just have to trust it (all bets are off if the local
- * DNS is perverted). We always check the address list, though.
- */
-
- if (strcasecmp(remotehost, hp->h_name)
- && strcasecmp(remotehost, "localhost")) {
- syslog(LOG_ERR, "host name/name mismatch: %s != %s",
- remotehost, hp->h_name);
- return (BAD);
- }
-
- /*
- * Look up the host address in the address list we just got.
- */
-
- for (i = 0; hp->h_addr_list[i]; i++) {
- if (memcmp(hp->h_addr_list[i], (caddr_t) & addr, sizeof(addr)) == 0)
- return (GOOD);
- }
-
- /*
- * The host name does not map to the original host address. Perhaps
- * someone has compromised a name server. More likely someone botched
- * it, but that could be dangerous, too.
- */
-
- syslog(LOG_ERR, "host name/address mismatch: %s != %s",
- inet_ntoa(addr), hp->h_name);
- return (BAD);
- }
- }
-
- /* udp_sink - absorb unreceived datagram */
-
- static void udp_sink()
- {
- char buf[BUFSIZ];
- struct sockaddr sa;
- int size = sizeof(sa);
-
- /*
- * Eat up the not-yet received packet. Some systems insist on a non-zero
- * source address argument in the recvfrom() call below.
- */
-
- (void) recvfrom(0, buf, sizeof(buf), 0, &sa, &size);
- }
-